-- Jednostka centralna

library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_Arith.all;
use work.constants.all;

entity CU3 is
  generic (delay : time := 5 ns);
  port (CLOCK, RESET            : in std_logic;
        INSTR, ADRARG1, ADRARG2 : in std_logic_vector(1 downto 0);
        ARG1, ARG2              : in std_logic_vector(3 downto 0);
        CU_GA                   : out std_logic_vector(2 downto 0);
        CU_ALU_INSTR, CU_ERROR  : out std_logic_vector(1 downto 0);
        CU_ROM_LAE, CU_ROM_R, CU_ROM_IE, CU_ROM_OE : out std_logic;
        CU_PC_INCR, CU_PC_RESET, CU_RI_LAE, CU_RN_LAE, CU_DE_DECODE : out std_logic;
        CU_RAM_LAE, CU_RAM_IE1, CU_RAM_IE2, CU_RAM_OE1, CU_RAM_OE2, CU_RAM_R, CU_RAM_W : out std_logic;
        CU_REGX_IE, CU_REGY_IE, CU_ALU_START : out std_logic;
        CU_RTMP_OE, CU_RTMP_IE : out std_logic;
        CU_R1_OE1, CU_R1_OE2, CU_R1_IE2 : out std_logic;
        CU_R2_OE1, CU_R2_OE2, CU_R2_IE2 : out std_logic;
        CU_R3_OE1, CU_R3_OE2, CU_R3_IE2 : out std_logic;
        CU_R4_OE1, CU_R4_OE2, CU_R4_IE2 : out std_logic;
        CU_R5_OE1, CU_R5_OE2, CU_R5_IE2 : out std_logic;
        CU_R6_OE1, CU_R6_OE2, CU_R6_IE2 : out std_logic);
end entity CU3;

architecture CU3_arch of CU3 is
begin
  process(CLOCK, RESET)
    variable state : integer := 0;
  begin
    if(RESET = '0') then
      state := 0;
      CU_ROM_OE <= '1';
      CU_RAM_OE1 <= '1';
      CU_RAM_OE2 <= '1';
      CU_RAM_IE1 <= '1';
      CU_RAM_IE2 <= '1';
      CU_ALU_START <= '1';
      CU_PC_RESET <= '0';
      CU_ERROR <= NO_ERROR;
    elsif(rising_edge(CLOCK)) then
      case state is
        when 0 =>
          CU_PC_RESET <= '1';
          CU_ALU_START <= '1';
          CU_RAM_OE1 <= '1';
          CU_RAM_W <= '1';
          CU_ROM_OE <= '1';
          CU_RAM_OE2 <= '1';
          CU_R1_IE2 <= '0';
          CU_R2_IE2 <= '0';
          CU_R3_IE2 <= '0';
          CU_R4_IE2 <= '0';
          CU_R5_IE2 <= '0';
          CU_R6_IE2 <= '0';
          --CU_R1_OE1 <= '1';
          --CU_R2_OE1 <= '1';
          --CU_R3_OE1 <= '1';
          --CU_R4_OE1 <= '1';
          --CU_R5_OE1 <= '1';
          --CU_R6_OE1 <= '1';
          CU_DE_DECODE <= '1';
           
          CU_GA <= GA_PC;
          CU_ROM_LAE <= '0';
          state := 1;
        when 1 =>
          CU_ROM_LAE <= '1';
          
          CU_PC_INCR <= '0';
          CU_ROM_R <= '0';
          CU_ROM_IE <= '0';
          state := 2;
        when 2 =>
          CU_PC_INCR <= '1';
          CU_ROM_R <= '1';
          CU_ROM_IE <= '1';
         
          CU_ROM_OE <= '0';
          CU_RI_LAE <= '0';
          CU_RN_LAE <= '0';
          state := 3;
        when 3 => 
          CU_ROM_OE <= '1';
          CU_RI_LAE <= '1';
          CU_RN_LAE <= '1';
          
          CU_DE_DECODE <= '0';
          state := 4;
        when 4 => 
          case INSTR is
            when CMP => state := 5;
            when MOV => state := 8;
            when others =>
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22; 
          end case;
          if (conv_integer(unsigned(ARG1)) > 12) then
            CU_ERROR <= ERR_OUT_OF_MEM; 
            state := 22;
          end if;
        when 5 => 
          CU_DE_DECODE <= '1';
          
          case ADRARG1 is
            -- tryb przemieszczeniowy
            when PRZ => CU_GA <= GA_IMM;
            -- tryb bazowy
            when BAZ => 
              case ARG1 is
                when "0000" =>
                  CU_GA <= GA_REG1;
                  CU_R1_OE1 <= '0';
                when "0001" =>
                  CU_GA <= GA_REG2;
                  CU_R2_OE1 <= '0';
                when "0010" => 
                  CU_R3_OE1 <= '0';
                  CU_GA <= GA_REG3;
                when "0011" =>
                  CU_GA <= GA_REG4;
                  CU_R4_OE1 <= '0';
                when "0100" =>
                  CU_GA <= GA_REG5;
                  CU_R5_OE1 <= '0';
                when "0101" =>
                  CU_GA <= GA_REG6;
                  CU_R6_OE1 <= '0';
                when others => 
                  CU_ERROR <= ERR_BAD_INSTR;
                  state := 22; 
              end case;
            when others =>
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22;  
          end case;
          CU_RAM_LAE <= '0';
          state := 6;
        when 6 =>
          CU_RAM_LAE <= '1';
          CU_R1_OE1 <= '1';
          CU_R2_OE1 <= '1';
          CU_R3_OE1 <= '1';
          CU_R4_OE1 <= '1';
          CU_R5_OE1 <= '1';
          CU_R6_OE1 <= '1';
           
          CU_RAM_R <= '0';
          CU_RAM_IE1 <= '0';
          state := 7;
        when 7 => 
          CU_RAM_R <= '1';
          CU_RAM_IE1 <= '1';
          
          CU_RAM_OE2 <= '0';
          CU_REGX_IE <= '0';
          state := 8;
        when 8 => 
          CU_DE_DECODE <= '1';
          CU_RAM_OE2 <= '1';
          CU_REGX_IE <= '1';
          
          CU_GA <= GA_PC;
          CU_ROM_LAE <= '0';
        
          state := 9;
        when 9 => 
          CU_ROM_LAE <= '1';
          
          CU_PC_INCR <= '0';
          CU_ROM_R <= '0';
          CU_ROM_IE <= '0';
          
          case ADRARG2 is
            when NAT => state := 15;
            when BAZ => state := 10;
            when others => 
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22;
          end case;
      when 10 =>
          CU_PC_INCR <= '1';
          CU_ROM_R <= '1';
          CU_ROM_IE <= '1';
          
          CU_ROM_OE <= '0';
          CU_RI_LAE <= '0';
          state := 11;
      when 11 =>
          CU_ROM_OE <= '1';
          CU_RI_LAE <= '1';
          
          CU_DE_DECODE <= '0';
          state := 12;
      when 12 =>
          CU_DE_DECODE <= '1';
          state := 13;
          
          if(ADRARG2 = BAZ and conv_integer(unsigned(ARG2)) > 5) then
            CU_ERROR <= ERR_BAD_INSTR;
            state := 22;
          end if;
      when 13 =>
          case ARG2 is
            when "0000" => CU_GA <= GA_REG1; CU_R1_OE1 <= '0';
            when "0001" => CU_GA <= GA_REG2; CU_R2_OE1 <= '0';
            when "0010" => CU_GA <= GA_REG3; CU_R3_OE1 <= '0';
            when "0011" => CU_GA <= GA_REG4; CU_R4_OE1 <= '0';
            when "0100" => CU_GA <= GA_REG5; CU_R5_OE1 <= '0';
            when "0101" => CU_GA <= GA_REG6; CU_R6_OE1 <= '0';
            when others => 
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22; 
            end case;
            CU_RAM_LAE <= '0';
            state := 14;
      when 14 =>
          CU_RAM_LAE <= '1';
          CU_R1_OE1 <= '1';
          CU_R2_OE1 <= '1';
          CU_R3_OE1 <= '1';
          CU_R4_OE1 <= '1';
          CU_R5_OE1 <= '1';
          CU_R6_OE1 <= '1';
          
          CU_RAM_R <= '0';
          CU_RAM_IE1 <= '0'; 
      when 15 =>
          CU_PC_INCR <= '1';
          CU_ROM_R <= '1';
          CU_ROM_IE <= '1';
          CU_RAM_R <= '1';
          CU_RAM_IE1 <= '1';
          
          case INSTR is
            when CMP => state := 16;
            when MOV => state := 18;
            when others =>
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22;
          end case;
          
        when 16 =>
          case ADRARG2 is
            when NAT => CU_ROM_OE <= '0';
            when BAZ => CU_RAM_OE2 <= '0';
            when others =>
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22;
          end case;
          state := 17;
          
        when 17 =>
          CU_RAM_OE2 <= '1';
          CU_ROM_OE <= '1';
         
          CU_ALU_INSTR <= CMP;
          CU_ALU_START <= '0';
          state := 0;
          
        when 18 =>
          case ADRARG1 is
            when REJ => state := 19;
            when PRZ => state := 20;
            when BAZ => state := 20;
            when others =>
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22;
          end case;
          
        when 19 =>
          case ADRARG2 is
            when NAT => CU_ROM_OE <= '0';
            when BAZ => CU_RAM_OE2 <= '0';
            when others =>
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22;
          end case;
          
          case ARG2 is
            when "0000" => CU_R1_IE2 <= '0';
            when "0001" => CU_R2_IE2 <= '0';
            when "0010" => CU_R3_IE2 <= '0';
            when "0011" => CU_R4_IE2 <= '0';
            when "0100" => CU_R5_IE2 <= '0';
            when "0101" => CU_R6_IE2 <= '0';
            when others => 
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22; 
          end case;
          state := 0;
          
        when 20 =>
          case ARG2 is
            when "0000" => CU_GA <= GA_REG1; CU_R1_OE1 <= '0';
            when "0001" => CU_GA <= GA_REG2; CU_R2_OE1 <= '0';
            when "0010" => CU_GA <= GA_REG3; CU_R3_OE1 <= '0';
            when "0011" => CU_GA <= GA_REG4; CU_R4_OE1 <= '0';
            when "0100" => CU_GA <= GA_REG5; CU_R5_OE1 <= '0';
            when "0101" => CU_GA <= GA_REG6; CU_R6_OE1 <= '0';
            when others => 
              CU_ERROR <= ERR_BAD_INSTR;
              state := 22; 
          end case;
          CU_RAM_LAE <= '0';
          state := 21;
          
        when 21 =>
          CU_RAM_LAE <= '1';
          CU_R1_OE1 <= '1';
          CU_R2_OE1 <= '1';
          CU_R3_OE1 <= '1';
          CU_R4_OE1 <= '1';
          CU_R5_OE1 <= '1';
          CU_R6_OE1 <= '1';
          
          CU_RAM_OE1 <= '0';
          CU_RAM_W <= '0';
          state := 0;
        
        when others =>
          CU_ERROR <= ERR_BAD_INSTR;
          state := 22;
      end case;
    end if;
  end process;
end architecture CU3_arch;
